package dataExport;

import data.DataVector;
import data.classDiagram.*;
import data.stateDiagram.ConditionalTransition;
import data.stateDiagram.State;
import data.stateDiagram.Transition;

import javax.swing.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * User: Szymon Kudzia
 * Date: 30.04.13
 * Time: 15:36
 */
public class JavaExporter implements Exporter {
	@Override
	public void export(String destination, ArrayList<Entity> entities) {
		BufferedWriter out = null;

		for (Entity entity : entities) {

			File fileEntity = new File(destination + File.separator + entity.getName() + ".java");

			try {
				fileEntity.createNewFile();
				out = new BufferedWriter(new FileWriter(fileEntity));

                String strEntity;

                if (entity instanceof ClassEntity)
                    strEntity = exportClassEntity((ClassEntity)entity);
                else
                    strEntity = exportInterfaceEntity((InterfaceEntity)entity);

				out.write(strEntity);

				out.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	@Override
	public String getName() {
		return "Export to Java";
	}


    private final String newLine = System.getProperty("line.separator");

    public String exportClassEntity(ClassEntity e) {
        StringBuilder buf = new StringBuilder();

        exportClassDefinition(e, buf);

        buf.append("{").append(newLine).append(newLine);

        exportClassMembers(e, buf);
        exportClassEnterStateMember(e, buf);

        buf.append(newLine);

        exportClassMethods(e, buf);
        exportClassStates(e, buf);

        buf.append("}");

        return buf.toString();
    }

    protected void exportClassStates(ClassEntity e, StringBuilder buf) {
        if (!e.getStateDiagram().getStates().isEmpty())
        {
            appClassTriggers(buf, e);
            appStateBaseAccessors(buf);
            appStateBase(buf, e);

            for (State s : e.getStateDiagram().getStates())
            {
                buf.append("\tpublic class ").append(s.getName()
                        .replaceAll(" ", "_")).append(" extends StateBase {")
                        .append(newLine);

                appTransition(buf, e, s);
                buf.append("\t}").append(newLine);
            }
        }
    }

    protected void exportClassMethods(ClassEntity e, StringBuilder buf) {
        boolean isSetterOrGetter = false;
        for (Method m : e.getMethods()) {
            for (String memberName : getMembersNames(e)) {
                if (m.getName().toLowerCase().contains(memberName.toLowerCase())
                        && (m.getName().startsWith("get") || m.getName().startsWith("set"))) {
                    isSetterOrGetter = true;
                }
            }

            exportMethod(buf, m, isSetterOrGetter);

            if (!isSetterOrGetter) {
                buf.append("{}");
            }
            isSetterOrGetter = false;
            buf.append(newLine);
        }
    }

    private void exportMethod(StringBuilder buf, Method m, boolean isSetterOrGetter)
    {
        String newLine = System.getProperty("line.separator");

        buf.append("\t");
        if (m.getVisibility().equals("+")){
            buf.append("public ");
        } else if (m.getVisibility().equals("-")){
            buf.append("private ");
        } else if (m.getVisibility().equals("#")){
            buf.append("protected ");
        }

        if (m.isStatic()){
            buf.append("static ");
        }

        buf.append(!m.getReturnType().isEmpty() ? m.getReturnType() : "void").append(" ");

        buf.append(m.getName()).append("(");

        for (int i = 0; i < m.getArguments().size() ; ++i){
            buf.append(m.getArguments().get(i).getTxtForm());
            if (i != m.getArguments().size()-1){
                buf.append(", ");
            }
        }

        buf.append(")");

        if (isSetterOrGetter){
            String memberName = m.getName().substring(3).toLowerCase();
            buf.append("{");
            buf.append(newLine);
            buf.append("\t\t");
            if (m.getName().startsWith("get")){
                buf.append("return ");
                buf.append(memberName).append(";");
            } else {
                buf.append("this.").append(memberName);
                buf.append(" = ").append(memberName).append(";");
            }
            buf.append(newLine).append("\t").append("}");
        }
    }

    private ArrayList<String> getMembersNames(ClassEntity e) {
        ArrayList<String> membersNames = new ArrayList<String>();

        for (Member m : e.getMembers()) {
            membersNames.add(m.getName());
        }

        return membersNames;
    }

    protected void exportClassMembers(ClassEntity e, StringBuilder buf) {
        for (Member m : e.getMembers()) {
            exportMember(buf, m);
        }
    }

    private void exportMember(StringBuilder buf, Member m) {
        buf.append("\t");

        if (m.getVisibility().equals("+")){
            buf.append("public ");
        } else if (m.getVisibility().equals("-")){
            buf.append("private ");
        } else if (m.getVisibility().equals("#")){
            buf.append("protected ");
        }

        if (m.isStatic()){
            buf.append("static ");
        }

        buf.append(m.getType()).append(" ");

        buf.append(m.getName());

        if (!m.getDefaultValue().isEmpty()){
            buf.append(" = ").append(m.getDefaultValue());
        }

        buf.append(";").append(System.getProperty("line.separator"));
    }

    protected void exportClassEnterStateMember(ClassEntity e, StringBuilder buf) {
        if (!e.getStateDiagram().getStates().isEmpty())
        {

            buf.append("\tprotected StateBase state");

            if (e.getStateDiagram().getEnterState() != null)
                buf.append(" = new ").append(e.getStateDiagram().getEnterState()
                        .getName()).append("()");

            buf.append(";");
	        buf.append(newLine);
        }
    }

    protected void exportClassDefinition(ClassEntity e, StringBuilder buf) {
        buf.append("public class ").append(getName()).append(" ");
        buf.append(!e.getBaseClass().isEmpty() ? "extends " + e.getBaseClass() + " " : "");

        if (!e.getInterfacesImplemented().isEmpty()) {
            buf.append("implements ");
            for (int i = 0; i < e.getInterfacesImplemented().size(); ++i) {
                buf.append(e.getInterfacesImplemented().get(i));
                if (i != e.getInterfacesImplemented().size() - 1) {
                    buf.append(", ");
                }
            }
        }
    }

    private void appClassTriggers(StringBuilder b, ClassEntity e)
    {
        final String newLine = System.getProperty("line.separator");

        for(Trigger t : e.getTriggers())
        {
            b.append("\tpublic void ").append(t.getName()).append("() { ")
                    .append(newLine).append("\t\tstate.").append(t.getName())
                    .append("(this);").append(newLine).append("\t}")
                    .append(newLine).append(newLine);
        }
    }

    private void appStateBaseAccessors(StringBuilder b)
    {
        b.append("\tpublic void setState(StateBase state) { ").append(newLine)
                .append("\t\tthis.state = state;").append(newLine)
                .append("\t}").append(newLine)
                .append(newLine).append("\tpublic StateBase getState() { ")
                .append(newLine).append("\t\treturn this.state;").append(newLine)
                .append("\t}").append(newLine).append(newLine);
    }

    private void appStateBase(StringBuilder b, ClassEntity e)
    {
        b.append("\tpublic class StateBase {");
        b.append(newLine);

        for(Trigger t : e.getTriggers())
            b.append("\t\tpublic void ").append(t.getName())
                    .append(" {}").append(newLine);

        b.append("\t}").append(newLine).append(newLine);
    }

    private void appTransition(StringBuilder b, ClassEntity e, State state)
    {
        for (Transition t : e.getStateDiagram().getTransitions())
        {
            if (t.getSource() != state) continue;
            if (t.getTrigger() == null) continue;

            appTab(b, 2);
            b.append("public void ").append(t.getTrigger().getName()).
                    append("(").append(getName()).append(" contex) {").append(newLine);

            if (t instanceof ConditionalTransition)
                appConditionalTransitionBody(b, (ConditionalTransition)t);
            else
                appTransitionBody(b, t);

            appTab(b, 2);
            b.append("}").append(newLine);
        }
    }

    private void appTransitionBody(StringBuilder b, Transition t)
    {
        boolean thereIsCondition = !t.getCondition().isEmpty();
        appTab(b, 3);

        if (thereIsCondition) {
            b.append("if (").append(t.getCondition()).append(") {").append(newLine);
            appTab(b, 4);
        }

        if (!t.getAction().isEmpty()) {
            b.append(t.getAction()).append(";").append(newLine);
            appTab(b, 3);
        }

        if (thereIsCondition)
            appTab(b, 1);

        b.append("contex.setState(new ").append(t.getDestination().getName().
                replaceAll(" ", "_")).append("());").append(newLine);

        if (thereIsCondition) {
            appTab(b, 3);
            b.append("}").append(newLine);
        }
    }

    private void appConditionalTransitionBody(StringBuilder b, ConditionalTransition ct)
    {
        appTab(b, 3);
        b.append("if (").append(ct.getCondition()).append(" == ").
                append(ct.getTrueExpression()).append(") {").append(newLine);

        if (!ct.getAction().isEmpty()) {
            appTab(b, 4);
            b.append(ct.getAction()).append(";").append(newLine);
        }

        appTab(b, 4);
        b.append("contex.setState(new ").append(ct.getDestination().
                getName().replaceAll(" ", "_")).append("());");

        b.append(newLine);
        appTab(b,3);
        b.append("}").append(newLine);

        if (!ct.getFalseExpression().isEmpty()) {
            appTab(b, 3);
            b.append("else if (").append(ct.getCondition()).append(" == ").
                    append(ct.getFalseExpression()).append(") {").append(newLine);

            if (!ct.getAction().isEmpty()) {
                appTab(b, 4);
                b.append(ct.getFailureAction()).append(";").append(newLine);
            }

            appTab(b,4);
            b.append("contex.setState(new ").append(ct.getFailureDestination().getName().
                    replaceAll(" ", "_")).append("());").append(newLine);

            appTab(b, 3);
            b.append("}").append(newLine);
        }
    }

    private void appTab(StringBuilder b, int count)
    {
        for (int i = 0; i < count; i++)
            b.append("\t");
    }



    protected String exportInterfaceEntity(InterfaceEntity e) {
        StringBuilder buf = new StringBuilder();
        final String newLine = System.getProperty("line.separator");

        buf.append(this.getClass().getPackage()).append(";").append(newLine).append(newLine);

        buf.append("public interface ").append(getName()).append(" ");

        if (!e.getInterfacesImplemented().isEmpty()) {
            buf.append("extends ");
            for (int i = 0; i < e.getInterfacesImplemented().size(); ++i) {
                buf.append(e.getInterfacesImplemented().get(i));
                if (i != e.getInterfacesImplemented().size()-1) {
                    buf.append(", ");
                }
            }
        }

        buf.append("{").append(newLine).append(newLine);

        for (Member m : e.getMembers()) {
            exportMember(buf, m);
        }

        buf.append(newLine);

        for (Method m : e.getMethods()) {
            exportMethod(buf, m, false);
            buf.append(";");
            buf.append(newLine);
        }

        buf.append("}");

        return buf.toString();
    }
}
